import means
import math
import numpy as np
import load
from gensim.models.doc2vec import Doc2Vec

benchmarkDiscount=1 # 即使完全一致，也只允许动差值的0.5
correctProp=0.3 # 给差值前多少修正

allReview=[]
allText=[]

def _getDist(p1,p2): # 俩词向量坐标算距离
    result=0
    for i in range(len(p1)):
        result+=(p1-p2)**2
    return result

def _getCos(vec1,vec2):
    return float(np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2)))

class review:
    def __init__(self,text:str,reliability:float,star:float,reviewID:str,date:float,id:str):
        self.text=text
        allText.append(text)
        self.reliability=reliability
        self.star=star
        self.vec=None
        self.reviewID=reviewID
        self.date=date
        self.id=id
        allReview.append(self)

    def getDist(self,r2):
        return _getDist(self.vec,r2.vec)

    def cos(self,r2):
        return _getCos(self.vec,r2.vec)


# 录入所有数据
for i in range(1, load.allNum):
    text=load.getReviewText(i)
    reliability=float(load.getCellT1(i, load.reliability))
    try:
        star=float(load.getCellT1(i, load.star))
    except:
        print('except')
        continue
    reviewID=load.getCellT1(i,load.reviewID)
    date=load.getDate(i)
    id=load.getCellT1(i,load.id)
    review(text,reliability,star,reviewID,date,id)

n_clusters=int(len(allReview)/50) # 50个相似的文本成为一个修正组

x_train = means.get_datasest(allText) # 编号
model_dm = means.train(x_train) # 训练词向量模型
# model_dm = Doc2Vec.load("model_dm.doc2vec")
allVec = means.getVec(x_train,model_dm)
allLabel, cluster_centers_ = means.cluster(allVec,n_clusters) # 得到doc向量并直接聚类
load.savePKL('doc向量聚类结果（奶嘴）.pkl', (allLabel, cluster_centers_))

# allLabel, cluster_centers_ = load.loadPKL('doc向量聚类结果.pkl')

allReviewClass={}
for i in range(n_clusters):
    allReviewClass[i]=[]
for i in range(len(allLabel)): # 按标签把allReview做成字典
    label=allLabel[i]
    allReview[i].vec=allVec[i] # vec放到对象里
    allReviewClass[label].append(allReview[i])

load.savePKL('allReviewClass（奶嘴）.pkl',allReviewClass)

# allReviewClass=load.loadPKL('allReviewClass.pkl')

# 对每一类的离群点进行修正
for i in range(n_clusters):
    center = cluster_centers_[i]
    ar = allReviewClass[i]

    avgStar=0
    for r in ar: # 算平均星
        avgStar+=r.star
    avgStar/=len(ar)

    ar.sort(key=lambda r:math.fabs(r.star-avgStar),reverse=True) # 按平均星差排序，降序，差值大的在前

    subBound=int(len(ar)*correctProp)

    # 对后面的计算平均评星差和信度，以准备修正
    avgStarDiff=0 # 要调的
    avgReliability=0 # 取值0-1
    for i in range(subBound,len(ar)):
        avgStarDiff+=math.fabs(ar[i].star-avgStar)
        avgReliability+=ar[i].reliability
    avgStarDiff/=len(ar)-subBound
    avgReliability/=len(ar)-subBound

    for i in range(subBound): # 找离群点并修正，（这里进行整体修正，类似批梯度下降）
        # 算与聚类中心的余弦相似度
        cos=_getCos(ar[i].vec,center)
        if cos>0 and cos<=1: # 只对相似的处理
            sim=1-cos
            diff=avgStar-ar[i].star
            print(diff)
            ar[i].star+=diff*benchmarkDiscount*avgReliability*sim # 可以迭代多次


result='review_id,product_id,modified star,reliability,date\n'
for i in range(n_clusters):
    ar = allReviewClass[i]
    for r in ar:
        result+=str(r.reviewID)+','+str(r.id)+','+str(r.star)+','+str(r.reliability)+','+str(r.date)+'\n'
print(result)
load.writeFile('SSML结果（奶嘴）.csv',result)